/* ==============================================================================
*
*   Copyright (c) 2010, Pierre Favier
*   All rights reserved.
*
*   NTasking is free software: you can redistribute it and/or modify it
*   under the terms of the GNU General Public License as published by the
*   Free Software Foundation.
*
*   Redistribution and use in source and binary forms, with or without
*   modification, are permitted provided that the following conditions
*   are met:
*
*   -   Redistributions of source code must retain the above copyright notice,
*       this list of conditions and the following disclaimer.
*
*   -   Redistributions in binary form must reproduce the above
*       copyright notice, this list of conditions and the following
*       disclaimer in the documentation and/or other materials
*       provided with the distribution.
*
*   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
*   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
*   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
*   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
*   BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
*   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
*   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
*   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
*   ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
*   TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
*   THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
*   SUCH DAMAGE.
*
* ============================================================================== */

using System;
using NUnit.Framework;
using Rhino.Mocks;

namespace NTasking.ValidationTests.ACATS_3_0.C9
{

    [TestFixture(Description = "")]
    public class C940006 : ValidationTest
    {

        //      Use a subset of the simulation of the freeway on-ramp described in
        //      c940005.  In this case two protected objects are used but only a
        //      minimum of routines are shown in each.  Both objects are hard coded
        //      and detail two adjacent on-ramps (Ramp_31 & Ramp_32) with routines in 
        //      each which use external calls to the  other.  


        private const int ClearLevel = 0;
        private const int MinimumLevel = 1;
        private const int ModerateLevel = 3;
        private const int SeriousLevel = 4;
        private const int CriticalLevel = 6;


        private static Ramp31 ramp31;
        private static Ramp32 ramp32;


        private class Ramp31 : Protected
        {

            private bool nextRampAlert = false; // Next Ramp is in trouble?

            // Current state of the various Sample Points
            private int localState = ClearLevel;
            // Accumulated load for next three downstream ramps
            private int dsrAccumulate = ClearLevel;


            [ReadWriteOperation]
            internal Action<int> SetLocalOverload { get; private set; }

            [ReadWriteOperation]
            internal Action DownstreamRamps { get; private set; }

            [ReadWriteOperation]
            internal Action Notify { get; private set; }


            [ReadOnlyOperation]
            internal Func<int> LocalOverload { get; private set; }

            [ReadOnlyOperation]
            internal Func<int> NextRampOverload { get; private set; }

            [ReadOnlyOperation]
            internal Func<int> FreewayOverload { get; private set; }

            [ReadOnlyOperation]
            internal Func<int> GetDSRAccumulate { get; private set; }


            public Ramp31()
            {
                // These Set/Clear routines are triggered by real-time sensors that
                // reflect traffic state
                ReadWriteOperation(SetLocalOverload, Is: sensorLevel =>
                {
                    // Notify "previous" ramp to check this one for current state.
                    // Subsequent changes in state will not send an alert 
                    ; //::::: (see Ramp_32 for this code)
                    localState = sensorLevel;
                    ; //::::: Start local meter if not already started

                });


                ReadOnlyOperation(LocalOverload, Is: () =>
                {
                    return localState;
                });


                // return the summation of all the load factors
                ReadOnlyOperation(FreewayOverload, Is: () =>
                {
                    // EACH IS A CALL OF A FUNCTION FROM WITHIN A FUNCTION
                    return LocalOverload() + NextRampOverload();
                });


                // This is notification from the next ramp that it is in
                // overload.  With this provision we only need to sample the next
                // ramp during adverse conditions.  
                ReadWriteOperation(Notify, Is: () =>
                {
                    nextRampAlert = true;
                });


                ReadOnlyOperation(NextRampOverload, Is: () =>
                {
                    if (nextRampAlert)
                        // EXTERNAL FUNCTION CALL FROM FUNCTION
                        // Get next ramp's current state
                        return ramp32.LocalOverload();
                    else
                        return ClearLevel;
                });


                // Snapshot the states of the next three downstream ramps
                ReadWriteOperation(DownstreamRamps, Is: () =>
                {
                    dsrAccumulate = ramp32.LocalOverload();  // EXTERNAL FUNCTION
                    // ::::     ramp33.LocalOverload() + // FROM  PROCEDURE
                    // ::::     ramp34.LocalOverload();
                });


                // Get last snapshot
                ReadOnlyOperation(GetDSRAccumulate, Is: () =>
                {
                    return dsrAccumulate;
                });
            }

        }


        private class Ramp32 : Protected
        {

            // Current state of the various Sample Points
            private int localState = ClearLevel;


            [ReadWriteOperation]
            internal Action<int> SetLocalOverload { get; private set; }


            [ReadOnlyOperation]
            internal Func<int> LocalOverload { get; private set; }


            public Ramp32()
            {
                // These Set/Clear routines are triggered by real-time sensors that
                // reflect traffic state
                ReadWriteOperation(SetLocalOverload, Is: sensorLevel =>
                {
                    if (localState == ClearLevel)
                    {
                        // Notify "previous" ramp to check this one for current state.
                        // Subsequent changes in state will not send an alert
                        // When the situation clears another routine performs the 
                        // all_clear notification. (not shown)
                        // EXTERNAL CALL OF PROCEDURE FROM PROCEDURE
                        ramp31.Notify();
                    }
                    localState = sensorLevel;
                    ; //::::: Start local meter if not already started
                });


                ReadOnlyOperation(LocalOverload, Is: () =>
                {
                    return localState;
                });
            }

        }


        [Test(Description =
                "Check that the body of a protected function can have external calls" +
                "to other protected functions and that the body of a protected" +
                "procedure can have external calls to protected procedures and to " +
                "protected functions.")]
        public void Test()
        {
            IValidationTestResult result = Mocks.DynamicMock<IValidationTestResult>();

            Ada.Declare(() =>
            {
                ramp31 = new Ramp31();
                ramp32 = new Ramp32();

                // Setup text expectations

                Expect.Call(result.TestFailed).Repeat.Never();
                Mocks.ReplayAll();
            },
            Begin: () =>
            {
                // Start test execution

                // Test driver.  This is ALL test control code
                // Simulate calls to the protected functions and procedures
                // from without the protected object, these will, in turn make the
                // external calls.

                // Check initial conditions, exercising the simple calls
                if (ramp31.LocalOverload() != ClearLevel ||
                    ramp31.NextRampOverload() != ClearLevel ||
                    ramp31.FreewayOverload() != ClearLevel ||
                    ramp32.LocalOverload() != ClearLevel)
                {
                    Console.WriteLine("Initial Calls provided unexpected Results");
                    result.TestFailed();
                }

                // Simulate real-time sensors reporting overloads at a hardware level
                ramp31.SetLocalOverload(1);
                ramp32.SetLocalOverload(3);

                ramp31.DownstreamRamps();

                if (ramp31.LocalOverload() != MinimumLevel ||
                    ramp31.GetDSRAccumulate() != ModerateLevel ||
                    ramp31.FreewayOverload() != SeriousLevel)
                {
                    Console.WriteLine("Secondary Calls provided unexpected Results");
                    result.TestFailed();
                }
            });
        }

    }

}
